#include <linux/module.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/types.h>

#define ADS8698_NUM_CHANNELS 8

#define ADS8698_CMD_REG(x) (x << 8)
#define ADS8698_CMD_REG_NOOP 0x00
#define ADS8698_CMD_REG_RST 0x85
#define ADS8698_CMD_REG_MAN_CH(chan) (0xC0 | (4 * chan))
#define ADS8698_CMD_DONT_CARE_BITS 16

#define ADS8698_PROG_REG(x) (x << 9)
#define ADS8698_PROG_REG_RANGE_CH(chan) (0x05 + chan)
#define ADS8698_PROG_WR_BIT BIT(8)
#define ADS8698_PROG_DONT_CARE_BITS 8

static const struct spi_device_id ads8698_id[] = {
    {"ads8698", 0},
    {},
};

/*定义设备树匹配表*/
static const struct of_device_id ads8698table[] = {
    {.compatible = "ti,ads8698-A"},
    {.compatible = "ti,ads8698-B"},
    {.compatible = "ti,ads8698-C"},
    {/* sentinel */}};

struct ads8698_data
{
    struct iio_dev *indio_dev;
    struct spi_device *spi;
    union
    {
        __be32 d32;
        u8 d8[4];
    } data[2] ____cacheline_aligned;
};

static DEFINE_MUTEX(lock);

#define ADS8698_VOLTAGE_CHAN(index)                   \
    {                                                 \
        .type = IIO_VOLTAGE,                          \
        .indexed = 1,                                 \
        .channel = index,                             \
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
        .scan_index = index,                          \
        .scan_type = {                                \
            .sign = 'u',                              \
            .realbits = 16,                           \
            .storagebits = 16,                        \
            .endianness = IIO_BE,                     \
        },                                            \
    }

static const struct iio_chan_spec ads8698_channels[ADS8698_NUM_CHANNELS] = {
    ADS8698_VOLTAGE_CHAN(0),
    ADS8698_VOLTAGE_CHAN(1),
    ADS8698_VOLTAGE_CHAN(2),
    ADS8698_VOLTAGE_CHAN(3),
    ADS8698_VOLTAGE_CHAN(4),
    ADS8698_VOLTAGE_CHAN(5),
    ADS8698_VOLTAGE_CHAN(6),
    ADS8698_VOLTAGE_CHAN(7),
};

static int ads8698_prog_read(struct iio_dev *indio_dev, unsigned int addr,
                             unsigned int *val)
{
    struct ads8698_data *data = iio_priv(indio_dev);
    struct spi_transfer t;
    uint32_t tmp;

    tmp = ADS8698_PROG_REG(addr);
    tmp <<= ADS8698_PROG_DONT_CARE_BITS;
    data->data[0].d32 = cpu_to_be32(tmp);

    // printk("data->data[0].d8[0]=%#x\n", data->data[0].d8[0]);
    // printk("data->data[0].d8[1]=%#x\n", data->data[0].d8[1]);
    // printk("data->data[0].d8[2]=%#x\n", data->data[0].d8[2]);
    // printk("data->data[0].d8[3]=%#x\n", data->data[0].d8[3]);
    // printk("data->data[0].d32=%#x\n", data->data[0].d32);

    t.tx_buf = &data->data[0].d8[1];
    t.rx_buf = &data->data[1].d8[1];
    t.len = 3;

    mutex_lock(&lock);

    spi_sync_transfer(data->spi, &t, 1);

    mutex_unlock(&lock);

    data->data[1].d32 &= 0xff000000; // 只需要保留接收的第三字节

    // printk("data->data[1].d8[0]=%#x\n", data->data[1].d8[0]);
    // printk("data->data[1].d8[1]=%#x\n", data->data[1].d8[1]);
    // printk("data->data[1].d8[2]=%#x\n", data->data[1].d8[2]);
    // printk("data->data[1].d8[3]=%#x\n", data->data[1].d8[3]);
    // printk("data->data[1].d32=%#x\n", data->data[1].d32);

    // printk("be32_to_cpu(data->data[1].d32=%#x\n", be32_to_cpu(data->data[1].d32));

    *val = (unsigned int)be32_to_cpu(data->data[1].d32);

    return 0;
}

static int ads8698_prog_write(struct iio_dev *indio_dev, unsigned int addr,
                              unsigned int val)
{
    struct ads8698_data *data = iio_priv(indio_dev);
    uint32_t tmp;
    int ret;

    tmp = ADS8698_PROG_REG(addr) | ADS8698_PROG_WR_BIT | val;
    tmp <<= ADS8698_PROG_DONT_CARE_BITS;
    data->data[0].d32 = cpu_to_be32(tmp);

    mutex_lock(&lock);

    ret = spi_write(data->spi, &data->data[0].d8[1], 3);

    mutex_unlock(&lock);

    return ret;
}

static int ads8698_reset(struct iio_dev *indio_dev)
{
    struct ads8698_data *data = iio_priv(indio_dev);
    u32 tmp;
    unsigned int rxval;

    tmp = ADS8698_CMD_REG(ADS8698_CMD_REG_RST);
    tmp <<= ADS8698_CMD_DONT_CARE_BITS;
    data->data[0].d32 = cpu_to_be32(tmp);

    mutex_lock(&lock);

    spi_write(data->spi, &data->data[0].d8[0], 4);

    mutex_unlock(&lock);

    ads8698_prog_write(indio_dev, 3, 1);

    ads8698_prog_read(indio_dev, 3, &rxval);

    printk("Feature Select = %hu\n", rxval);
    return 0;
}

static int ads8698_read(struct iio_dev *indio_dev, unsigned int chan, int *val)
{
    struct ads8698_data *data = iio_priv(indio_dev);
    int ret;
    u32 tmp;
    u8 txtbuf[5];
    u8 rxtbuf[5];
    u8 rxrbuf[5];

    memset(txtbuf, 0, 5);
    memset(rxtbuf, 0, 5);
    memset(rxrbuf, 0, 5);

    struct spi_transfer t[] = {
        {
            // .tx_buf = &data->data[0].d8[0],
            .tx_buf = txtbuf,
            .len = 5,
            .cs_change = 1,
        },
        {
            // .tx_buf = &data->data[1].d8[0],
            // .rx_buf = &data->data[1].d8[0],
            .tx_buf = rxtbuf,
            .rx_buf = rxrbuf,
            .len = 5,
        },
    };

    tmp = ADS8698_CMD_REG(ADS8698_CMD_REG_MAN_CH(chan));
    tmp <<= ADS8698_CMD_DONT_CARE_BITS;
    data->data[0].d32 = cpu_to_be32(tmp);
    memcpy(txtbuf, data->data[0].d8, 4);

    tmp = ADS8698_CMD_REG(ADS8698_CMD_REG_NOOP);
    tmp <<= ADS8698_CMD_DONT_CARE_BITS;
    data->data[1].d32 = cpu_to_be32(tmp);

    mutex_lock(&lock);
    ret = spi_sync_transfer(data->spi, t, ARRAY_SIZE(t));
    mutex_unlock(&lock);

    if (ret < 0)
        return ret;

    memset(data->data[1].d8, 0, 4);
    memcpy(data->data[1].d8, &rxrbuf[2], 3);

    // printk("rxrbuf[2]=%#x\n",rxrbuf[2]);
    // printk("rxrbuf[3]=%#x\n",rxrbuf[3]);
    // printk("rxrbuf[4]=%#x\n",rxrbuf[4]);

    // printk("data->data[1].d8[0]=%#x\n",data->data[1].d8[0]);
    // printk("data->data[1].d8[1]=%#x\n",data->data[1].d8[1]);
    // printk("data->data[1].d8[2]=%#x\n",data->data[1].d8[2]);
    // printk("data->data[1].d8[3]=%#x\n",data->data[1].d8[3]);
    // printk("be32_to_cpu(data->data[1].d32)=%#x\n",be32_to_cpu(data->data[1].d32)>>14);

    // printk("rxval = %#X\n", ((rxrbuf[2] << 16) | (rxrbuf[3] << 8) | (rxrbuf[4])) >> 6);
    // printk("ChannelNum=%hhu\n", (rxrbuf[4] & 0x3f) >> 2);

    *val = be32_to_cpu(data->data[1].d32) >> 14;
    return 0;
}

static int ads8698_read_raw(struct iio_dev *indio_dev,
                            struct iio_chan_spec const *chan,
                            int *val, int *val2, long mask)
{
    int ret;
    switch (mask)
    {
    case IIO_CHAN_INFO_RAW:
        ret = ads8698_read(indio_dev, chan->channel, val);
        if (ret < 0)
            return ret;
        return IIO_VAL_INT;
    default:
        return -EINVAL;
    }
}

static const struct iio_info ads8698_info = {
    .read_raw = ads8698_read_raw,
};

static irqreturn_t ads8698_trigger_handler(int irq, void *p)
{
    printk("ads8698_trigger_handler\n");
    struct iio_poll_func *pf = p;
    struct iio_dev *indio_dev = pf->indio_dev;
    int buffer[ADS8698_NUM_CHANNELS + sizeof(s64) / sizeof(u16)];
    int i, j = 0;

    for (i = 0; i < indio_dev->masklength; i++)
    {
        if (!test_bit(i, indio_dev->active_scan_mask))
            continue;
        ads8698_read(indio_dev, i, &buffer[j]);
        j++;
    }

    iio_push_to_buffers_with_timestamp(indio_dev, buffer,
                                       iio_get_time_ns(indio_dev));

    iio_trigger_notify_done(indio_dev->trig);

    return IRQ_HANDLED;
}

static int ads8698_probe(struct spi_device *spi)
{
    struct ads8698_data *data;
    struct iio_dev *indio_dev;
    int ret;

    const struct of_device_id *match;

    match = of_match_device(ads8698table, &spi->dev);

    printk("ads8698_probe\n");

    spi->mode = SPI_MODE_0;
    spi->bits_per_word = 8;
    spi_setup(spi);

    indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
    if (!indio_dev)
        return -ENOMEM;

    data = iio_priv(indio_dev);
    data->indio_dev = indio_dev;
    data->spi = spi;
    spi_set_drvdata(spi, indio_dev);

    indio_dev->dev.parent = &spi->dev;
    indio_dev->info = &ads8698_info;
    indio_dev->name = match->compatible;
    indio_dev->channels = ads8698_channels;
    indio_dev->num_channels = ARRAY_SIZE(ads8698_channels);
    indio_dev->modes = INDIO_DIRECT_MODE;

    ads8698_reset(indio_dev);

    // ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, ads8698_trigger_handler, NULL);
    // if (ret < 0)
    // {
    //     dev_err(&spi->dev, "iio triggered buffer setup failed\n");
    //     return ret;
    // }

    ret = devm_iio_device_register(&spi->dev, indio_dev);
    if (ret)
    {
        dev_err(&spi->dev, "iio device register failed\n");
        return ret;
    }

    return 0;
}

static int ads8698_remove(struct spi_device *spi)
{
    printk("ads8698_remove\n");

    return 0;
}

static struct spi_driver ads8698_driver = {
    .driver = {
        .name = "ads8698",
        .owner = THIS_MODULE,
        .of_match_table = ads8698table,
    },
    .probe = ads8698_probe,
    .remove = ads8698_remove,
    .id_table = ads8698_id,
};
// module_spi_driver(ads8698_driver);

/*
 *驱动初始化函数
 */
static int __init ads8698_driver_init(void)
{
    int ret;
    pr_info("ads8698_driver_init\n");
    ret = spi_register_driver(&ads8698_driver);
    return ret;
}

/*
 *驱动注销函数
 */
static void __exit ads8698_driver_exit(void)
{
    pr_info("ads8698_driver_exit\n");
    spi_unregister_driver(&ads8698_driver);
}

module_init(ads8698_driver_init);
module_exit(ads8698_driver_exit);

MODULE_AUTHOR("JXZG zxz");
MODULE_DESCRIPTION("ADS8698 driver");
MODULE_LICENSE("GPL");
